<?php

namespace Tbf\Component\FileSystem;
use Tbf\Component\Crypto\CryptoRandUtil;
use Tbf\Util\PathUtil;
/**
 * 文件系统的一些操作封装
 * 默认行为：
 *     所有文件操作，默认行为均为覆盖。（尽最大努力删除旧的，写入新的。。）
 *     所有操作默认均为递归执行。
 * TODO: 错误处理
 */
final class FileSystemFunction
{
    /**
     * 递归拷贝文件
     */
    public static function copy($origin,$target,$override = true)
    {
        return self::copyByCallBack($origin,$target,$override);
    }
    /**
     * 递归拷贝文件。
     * 使用回调确定是否拷贝该文件。
     * func (path,isDir)(needCopy)
     */
    public static function copyByCallBack($origin,$target,$override = true,$cb=null)
    {
        if (is_file($origin)) {
            return self::copyOneFile($origin,$target,$override);
        }
        if ($cb===null) {
            $cb = function(){
                return true;
            };
        }
        PathUtil::listFileRByCallback($origin,
            function($path,$isDir) use ($origin,$target,$override,$cb) {
                if (!$cb($path,$isDir)) {
                    return false;
                }
                $ref = PathUtil::refPath($path,$origin);
                $thisOrigin = $path;
                $thisTarget = $target.'/'.$ref;
                self::copyOneFile($thisOrigin,$thisTarget,$override);

                return true;
            });
    }
    /**
     * 拷贝一个文件
     */
    public static function copyOneFile($origin,$target,$override = true)
    {
        //不覆盖
        if ( !$override && file_exists($target)) {
            return null;
        }
        //创建目录
        $err = self::mkdir(dirname($target));
        if ($err!==null) {
            return $err;
        }
        if ( is_dir($origin)) {
            $err = self::mkdir($target);
            if ($err!==null) {
                return $err;
            }
        } else {
            copy($origin,$target);
        }

        return null;
    }
    /**
     * 递归创建目录,如果目录存在，则什么也不做
     * 如果选择覆盖，如果这位置有一个文件不是目录，则删除该文件，并创建目录
    @param string $path
     * @return error
     */
    public static function mkdir($path,$override = true,$mode = 0777)
    {
        //正常情况 快速通过
        $ret = @mkdir($path,$mode,true);
        if ($ret===true) {
            //创建成功
            return null;
        }
        //如果没有权限,则抛出异常
        $error = error_get_last();
        $error = $error['message'];
        if ($error!=='mkdir(): File exists') {
            return 'mkdir fail! path:'.$path.' error: '.$error;
        }
        //如果目录存在.则什么也不做
        if (is_dir($path)) {
            return null;
        }
        //如果是文件,并且需要覆盖,则删除文件
        if (file_exists($path)) {
            if (!$override) {
                return null;
            }
            self::delete($path);
            $err = self::mkdir($path,$override,$mode);
            if ($err!==null) {
                return $err;
            }

            return null;
        }

        return 'mkdir fail! should not run to this line, path:'.$path;
    }
    /**
     * 建立软链接
     */
    public static function symlink($origin,$target,$override = true)
    {
        if (file_exists($target)) {
            if ($override) {
                self::delete($target);
            } else {
                return;
            }
        }
        symlink($origin,$target);
    }
    /**
     * 删除文件或文件夹
     * 1.文件不存在不报错
     * 2.没有权限会报php error
     * 3.无论文件的内容是什么,一定删除
     * @return error
     */
    public static function delete($path,$deep = 10)
    {
        if ($deep<0) {
            return 'delete file too deep path:'.$path;
        }
        //文件不存在。。
        if (!file_exists($path)) {
            return null;
        }
        if (self::isPlainDir($path)) {
            $list = PathUtil::listDir($path,array('.git'=>null));
            foreach ($list as $v1) {
                $err = self::delete($v1,$deep-1);
                if ($err!==null) {
                    return $err;
                }
            }
            $ret = rmdir($path);
            if ($ret) {
                return null;
            }

            return 'can not delete dir path:'.$path;
        }
        $ret = unlink($path);
        if ($ret) {
            return null;
        }

        return 'can not delete path:'.$path;
    }
    /**
     * 写入文件
     * 默认
     * 1.目录不存在创建目录
     * 2.覆盖原有文件
     * 配置
     * 1.isMkdir 是否创建目录？ 是
     * 2.override 是否覆盖？  是
     * @return error
     * TODO 错误处理
     */
    public static function putContent($path,$content,$option=array())
    {
        $default = array('override'=>true,
            'isMkdir'=>true);
        $option = array_merge($default,$option);
        //不覆盖

        if ( !$option['override'] && file_exists($path)) {
            return null;
        }
        //创建目录
        if ($option['isMkdir']) {
            $err = self::mkdir(dirname($path));
            if ($err!==null) {
                return $err;
            }
        }
        file_put_contents($path,$content);

        return null;
    }
    /**
     * 是普通目录？
     */
    public static function isPlainDir($path)
    {
        return is_dir($path)&&(!is_link($path));
    }

    /**
     * 移动文件
     * 1.目录不存在创建目录
     * 2.覆盖目标文件
     * 3.错误处理
     * bool 是否成功
     * @return error
     * TODO 错误处理
     */
    public static function move($origin_path,$dest_path)
    {
        $err = self::mkdir(dirname($dest_path));
        if ($err!==null) {
            return $err;
        }
        rename($origin_path,$dest_path);

        return null;
    }

    /**
     * 文件大小,加入错误处理
     * @param $path
     * @return int
     * @return error
     * TODO 错误处理
     */
    public static function size($path)
    {
        return array(filesize($path),null);
    }
    /**
     * 文件内容,加入错误处理
     * @param $path
     * @return int
     * @return error
     * TODO 错误处理
     */
    public static function getContent($path)
    {
        return array(file_get_contents($path),null);
    }

    /**
     * 返回一个临时文件的路径
     */
    public static function getTmpFilePath($extension=null){
        if ($extension===null){
            return '/tmp/'.CryptoRandUtil::hex(10);
        }else{
            return '/tmp/'.CryptoRandUtil::hex(10).'.'.$extension;
        }
    }
}
